//plugin use gui mode

package main

import (
	"fmt"
	"math/rand"
	"os"
	"path/filepath"
	"strings"
	"time" //"github.com/gotk3/gotk3/gdk"

	"github.com/gotk3/gotk3/glib"
	"github.com/gotk3/gotk3/gtk"
	"github.com/rocket049/gettext-go/gettext"
)

func init() {
	rand.Seed(time.Now().UnixNano())
	verbose = true
	step = 1
	exe1, _ := os.Executable()
	dir1 := filepath.Dir(exe1)
	gettext.BindTextdomain("mimichat", filepath.Join(dir1, "locale"), nil)
	gettext.Textdomain("mimichat")
}

func T(s string) string {
	return gettext.PGettext("", s)
}

var (
	buf     string
	bot     *Bot
	step    int
	verbose bool
)

type chatWindow struct {
	win         *gtk.Window
	grid        *gtk.Grid
	msgView     *gtk.TreeView
	msgStore    *gtk.ListStore
	userView    *gtk.TreeView
	userStore   *gtk.ListStore
	inputBox    *gtk.Entry
	userMsg     *gtk.Label
	target      *gtk.Label
	scrollLeft  *gtk.ScrolledWindow
	scrollRight *gtk.ScrolledWindow
	msgRender   *gtk.CellRendererText
}

//const 列表成员
const (
	MsgSender int = iota
	MsgText
	MsgColor
	MsgWidth
)

func (s *chatWindow) Create() {
	var err error
	s.win, err = gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
	if err != nil {
		panic(err)
	}
	s.win.SetTitle(T("Security IRC Chat"))
	s.win.SetIconName("stock_internet")
	s.grid, err = gtk.GridNew()
	if err != nil {
		panic(err)
	}
	s.win.Add(s.grid)

	s.userMsg, err = gtk.LabelNew(T("User Message"))
	if err != nil {
		panic(err)
	}
	s.grid.Attach(s.userMsg, 0, 0, 2, 1)

	s.scrollLeft, err = gtk.ScrolledWindowNew(nil, nil)
	if err != nil {
		panic(err)
	}
	s.scrollLeft.SetSizeRequest(500, 400)
	s.scrollLeft.SetHExpand(true)
	s.scrollLeft.SetVExpand(true)
	s.scrollLeft.SetPolicy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
	s.grid.Attach(s.scrollLeft, 0, 1, 1, 1)
	s.msgView, err = gtk.TreeViewNew()
	if err != nil {
		panic(err)
	}
	s.msgView.SetEnableTreeLines(true)
	s.scrollLeft.Add(s.msgView)

	s.scrollRight, err = gtk.ScrolledWindowNew(nil, nil)
	if err != nil {
		panic(err)
	}
	s.scrollRight.SetSizeRequest(100, 400)
	s.scrollRight.SetVExpand(true)
	s.grid.Attach(s.scrollRight, 1, 1, 1, 1)
	s.userView, err = gtk.TreeViewNew()
	if err != nil {
		panic(err)
	}
	s.scrollRight.Add(s.userView)

	s.inputBox, err = gtk.EntryNew()
	if err != nil {
		panic(err)
	}
	s.grid.Attach(s.inputBox, 0, 2, 1, 1)

	s.target, err = gtk.LabelNew("#")
	if err != nil {
		panic(err)
	}
	s.grid.Attach(s.target, 1, 2, 1, 1)
	s.grid.SetColumnSpacing(3)
	s.grid.SetRowSpacing(3)
	s.grid.ShowAll()

	s.setMsgView()
	s.setUserView()
	s.setSignals()

	s.win.Show()
}

func (s *chatWindow) setMsgView() {
	//initial s.msgView
	var err error
	s.msgStore, err = gtk.ListStoreNew(glib.TYPE_STRING, glib.TYPE_STRING, glib.TYPE_STRING)
	if err != nil {
		panic(err)
	}
	render, _ := gtk.CellRendererTextNew()
	colSender, err := gtk.TreeViewColumnNewWithAttribute(T("Sender"), render, "text", MsgSender)
	if err != nil {
		panic(err)
	}
	colSender.AddAttribute(render, "background", MsgColor)
	w, _ := s.scrollRight.GetSizeRequest()
	colSender.SetFixedWidth(w)
	render.SetProperty("wrap-width", w)

	s.msgRender, _ = gtk.CellRendererTextNew()
	wleft, _ := s.scrollLeft.GetSizeRequest()
	s.msgRender.SetProperty("wrap-width", wleft-w-20)
	colMsg, err := gtk.TreeViewColumnNewWithAttribute(T("Message"), s.msgRender, "text", MsgText)
	if err != nil {
		panic(err)
	}

	colMsg.AddAttribute(s.msgRender, "background", MsgColor)

	s.msgView.SetTooltipText(T("Double click to copy message to input box."))

	s.msgView.SetGridLines(gtk.TREE_VIEW_GRID_LINES_BOTH)
	s.msgView.SetModel(s.msgStore)
	s.msgView.AppendColumn(colSender)
	s.msgView.AppendColumn(colMsg)
}

func (s *chatWindow) setUserView() {
	//initial s.userView
	var err error
	s.userStore, err = gtk.ListStoreNew(glib.TYPE_STRING, glib.TYPE_STRING)
	if err != nil {
		panic(err)
	}
	render, _ := gtk.CellRendererTextNew()
	w, _ := s.scrollRight.GetSizeRequest()
	render.SetProperty("wrap-width", w)

	colUser, err := gtk.TreeViewColumnNewWithAttribute(T("Users"), render, "text", 0)
	if err != nil {
		panic(err)
	}
	colUser.AddAttribute(render, "background", 1)

	s.userView.AppendColumn(colUser)
	s.userView.SetModel(s.userStore)
}

func (s *chatWindow) appendMsg(sender, msg, color string) {
	//var iter gtk.TreeIter
	glib.IdleAdd(func() bool {
		p := s.msgStore.Append()
		s.msgStore.SetValue(p, MsgSender, sender)
		s.msgStore.SetValue(p, MsgText, msg)
		s.msgStore.SetValue(p, MsgColor, color)

		glib.IdleAdd(func() bool {
			vadj := s.scrollLeft.GetVAdjustment()
			//size := vadj.GetValue() + vadj.GetPageIncrement()
			//fmt.Println("VAdjustment:", vadj.GetValue(), vadj.GetPageIncrement())
			vadj.SetValue(vadj.GetUpper())
			return false
		})

		return false
	})
}

func (s *chatWindow) appendUser(name, color string) {
	//var iter gtk.TreeIter
	glib.IdleAdd(func() bool {
		p := s.userStore.Append()
		s.userStore.SetValue(p, 0, name)
		s.userStore.SetValue(p, 1, color)

		glib.IdleAdd(func() bool {
			vadj := s.scrollRight.GetVAdjustment()
			size := vadj.GetValue() + vadj.GetPageIncrement()
			//fmt.Println("VAdjustment:", vadj.GetValue(), vadj.GetPageIncrement())
			vadj.SetValue(size)
			return false
		})
		return false
	})
}

func (s *chatWindow) setSignals() {
	s.win.Connect("destroy", func() {
		bot.Close()
		fmt.Println("main quit")
	})
	s.win.Connect("show", func() {
		glib.IdleAdd(func() bool {
			s.getLoginInfo()
			return false
		})
	})
	s.inputBox.Connect("activate", func() {
		msg, err := s.inputBox.GetText()
		if err != nil {
			panic(err)
		}
		bot.PrivMsgTo(bot.Channel, msg)
		s.inputBox.SetText("")
		s.appendMsg(bot.Nick, msg, "#89D4E5")
	})
	s.msgView.Connect("row-activated", func(view *gtk.TreeView, path1 *gtk.TreePath, col1 *gtk.TreeViewColumn) {
		model, err := s.msgView.GetModel()
		panicError(err)
		iter, err := model.GetIter(path1)
		panicError(err)
		v0, err := model.GetValue(iter, 0)
		panicError(err)
		v1, err := model.GetValue(iter, 1)
		panicError(err)
		s0, err := v0.GetString()
		panicError(err)
		s1, err := v1.GetString()
		panicError(err)
		s.inputBox.SetText(fmt.Sprintf("%s:%s", s0, s1))
	})
}

func (s *chatWindow) getLoginInfo() {
	dlg, err := gtk.DialogNew()
	if err != nil {
		panic(err)
	}
	dlg.SetTitle(T("Login"))
	dlg.SetTransientFor(s.win)

	box, err := dlg.GetContentArea()
	if err != nil {
		panic(err)
	}
	grid, err := gtk.GridNew()
	if err != nil {
		panic(err)
	}
	box.PackStart(grid, true, true, 10)
	label, err := gtk.LabelNew(T("Server:"))
	if err != nil {
		panic(err)
	}
	grid.Attach(label, 0, 0, 1, 1)
	server, err := gtk.EntryNew()
	if err != nil {
		panic(err)
	}
	server.SetText("chat.freenode.net:6667")
	server.SetWidthChars(25)
	grid.Attach(server, 1, 0, 1, 1)

	label, err = gtk.LabelNew(T("Room:"))
	if err != nil {
		panic(err)
	}
	grid.Attach(label, 0, 1, 1, 1)
	room, err := gtk.EntryNew()
	if err != nil {
		panic(err)
	}
	grid.Attach(room, 1, 1, 1, 1)

	label, err = gtk.LabelNew(T("Nick:"))
	if err != nil {
		panic(err)
	}
	grid.Attach(label, 0, 2, 1, 1)
	nick, err := gtk.EntryNew()
	if err != nil {
		panic(err)
	}
	nick.SetText(fmt.Sprintf("talker%d", rand.Uint32()))
	grid.Attach(nick, 1, 2, 1, 1)

	label, err = gtk.LabelNew(T("Key:"))
	if err != nil {
		panic(err)
	}
	grid.Attach(label, 0, 3, 1, 1)
	key, err := gtk.EntryNew()
	if err != nil {
		panic(err)
	}
	key.SetInvisibleChar(rune('*'))
	key.SetVisibility(false)
	grid.Attach(key, 1, 3, 1, 1)
	grid.ShowAll()

	dlg.AddButton(T("Login"), 1)
	dlg.AddButton(T("Cancel"), 2)

	rid := dlg.Run()

	if rid == 1 {
		bot = new(Bot)
		bot.server, err = server.GetText()
		if err != nil {
			panic(err)
		}
		bot.User, err = nick.GetText()
		bot.Nick = bot.User
		if err != nil {
			panic(err)
		}
		room1, err := room.GetText()
		if err != nil {
			panic(err)
		}
		bot.Channel = fmt.Sprintf("#xtalk%s", room1)
		s.target.SetText(bot.Channel)
		sk, err := key.GetText()
		if err != nil {
			panic(err)
		}
		bot.Crypto.SetKey(sk)
		step = 2
		s.userMsg.SetText(fmt.Sprintf("%s / %s / %s", bot.server, room1, bot.Nick))

		go s.doRecv()
	}
	dlg.Destroy()
	if step != 2 {
		gtk.MainQuit()
	}
}

//doRecv work in goroutine
func (s *chatWindow) doRecv() {
	bot.Connect()
	defer gtk.MainQuit()
	bot.Command("MODE", bot.Channel, "+s")
	for {
		message, err := bot.Recv()
		if err != nil {
			s.appendMsg("sys", "Exit Now!", "#FCBBBD")
			break
		}
		//fmt.Println(message.Command, message.Params)

		if message.Command == "JOIN" {
			//connected
			s.appendMsg(message.Prefix.Name, message.Command, "#21EFA9")
			if step < 3 && message.Prefix.Name == bot.Nick {
				step = 3
			}
			if message.Prefix.Name != bot.Nick {
				s.appendUser(message.Prefix.Name, "white")
			}
		} else if message.Command == "QUIT" {
			s.appendMsg(message.Prefix.Name, message.Command, "#FCBBBD")
			s.removeUser(message.Prefix.Name)
		} else if message.Command == "PING" {
			bot.Send(fmt.Sprintf("PONG %d", time.Now().UnixNano()))
			//log.Println("SEND: PONG")
		} else if message.Command == "PRIVMSG" {
			// Do Something with this msg
			rmsg := message.Params[1]
			dmsg := bot.Crypto.Decode(rmsg)
			name1 := message.Prefix.Name
			var msg string
			if dmsg == nil {
				msg = fmt.Sprintf("%s %s", T("[not secret]"), rmsg)
				s.appendMsg(string(name1), msg, "#FCBBBD")
			} else {
				msg = string(dmsg)
				s.appendMsg(string(name1), msg, "white")
			}
		} else if message.Command == "353" {
			names := strings.Split(message.Params[3], " ")
			for _, v := range names {
				s.appendUser(v, "white")
			}
		} else if verbose {
			//log.Printf("%v\n", message)
			msg := fmt.Sprintf("%s:%s", message.Command, strings.Join(message.Params, ","))
			s.appendMsg(message.Prefix.Name, msg, "#F9F9CB")
		}
	}
}

func (s *chatWindow) removeUser(name string) bool {
	model, err := s.userView.GetModel()
	panicError(err)
	iter, ok := model.GetIterFirst()
	if !ok {
		return false
	}
	v, err := model.GetValue(iter, 0)
	panicError(err)
	sv, err := v.GetString()
	panicError(err)
	if strings.Compare(sv, name) == 0 {
		return s.userStore.Remove(iter)
	}
	for model.IterNext(iter) {
		v, err := model.GetValue(iter, 0)
		panicError(err)
		sv, err := v.GetString()
		panicError(err)
		if strings.Compare(sv, name) == 0 {
			return s.userStore.Remove(iter)
		}
	}
	return false
}

func (s *chatWindow) Example() {
	s.appendMsg("tom", "hello", "white")
	s.appendMsg("jack", "hello", "#89D4E5")
	s.appendMsg("tom", "hello", "white")
	s.appendMsg("jack", "func (s *chatWindow) setSignals() func (s *chatWindow) setSignals() func (s *chatWindow) setSignals() ", "#89D4E5")
	s.appendMsg("jack", "func (s *chatWindow) setSignals() func (s *chatWindow) setSignals() func (s *chatWindow) setSignals() ", "white")
	s.appendMsg("jack", "func (s *chatWindow) setSignals() func (s *chatWindow) setSignals() func (s *chatWindow) setSignals() ", "#89D4E5")
	s.appendMsg("jack", "对于高性能分布式系统领域而言，Go 语言无疑比大多数其它语言有着更高的开发效率。它提供了海量并行的支持，这对于游戏服务端的开发而言是再好不过了。", "white")

	s.appendUser("jack", "#89D4E5")
	s.appendUser("tom", "white")
	s.appendUser("abcdefghijklmnopqrst", "#89D4E5")

	glib.IdleAdd(func() bool {
		model, err := s.userView.GetModel()
		panicError(err)
		iter, ok := model.GetIterFirst()
		if !ok {
			return false
		}
		v, err := model.GetValue(iter, 0)
		panicError(err)
		sv, err := v.GetString()
		panicError(err)
		fmt.Println(sv)
		for model.IterNext(iter) {
			v, err := model.GetValue(iter, 0)
			panicError(err)
			sv, err := v.GetString()
			panicError(err)
			fmt.Println(sv)
		}
		return false
	})
}

func panicError(err error) {
	if err != nil {
		panic(err)
	}
}

var (
	win1 *chatWindow
)

func RunMain() {
	//defer gettext.SaveLog()
	gtk.Init(&os.Args)
	win1 = new(chatWindow)
	win1.Create()
	gtk.Main()
}

func main() {}
